﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace VDataAdapter
{
    public class VdataMessageBuilder : VDataAdapter.IVdataMessageBuilder
    {

        public VdataMessageBuilder(){}
        
        
        
        /// <summary>
        /// Build the message MarketInfo struct included values and properties of new MarketInfo and old MarketInfo
        /// </summary>
        /// <param name="objNew">New market object </param>
        /// <param name="objOld">Old market object </param>
        /// <returns>Return a message string have struct along Market parameter</returns>
        public virtual string BuildMessageMarket(PriceLibrary.PriceService.MarketInfo objNew,
            PriceLibrary.PriceService.MarketInfo objOld)
        {
            string strMsgObj = MessageType.MarketInfo.ToString().ToUpper() + "#";
            string strNew = String.Empty;
            string strOld = String.Empty;

            try
            {
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.PriceService.MarketInfo));
                foreach (PropertyDescriptor prop in properties)
                {
                    if (prop.PropertyType.ToString() != PropertyTypeRejected)
                    {
                        strNew += GetPropertyMessageMarket(objNew, prop);
                        strOld += GetPropertyMessageMarket(objOld, prop);
                    }
                }
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strNew + "#" + strOld;
            }
            catch (Exception ex)
            {
                throw new Exception("Error building a Marketinfo message");
            }
            return strMsgObj;
        }


        string PropertyTypeRejected = "System.Runtime.Serialization.ExtensionDataObject";

        private string GetPropertyMessageMarket(PriceLibrary.PriceService.MarketInfo obj, PropertyDescriptor prop)
        {
            string strMsg = String.Empty;
            if (PropertyIsRejected(prop.PropertyType.ToString()))
                throw new Exception("PropertyTypeRejected exception GetPropertyMessageMarket: System.Runtime.Serialization.ExtensionDataObject");
     
            if (prop.Name == "TradingDate")
            {
                strMsg += "12:00:00" + "|" ?? DBNull.Value.ToString() + "|";

            }  
            else if (prop.Name == "TradingTime")
            {
                var time = int.Parse(prop.GetValue(obj).ToString());
                strMsg += time.ToString("00:00:00") + "|" ?? DBNull.Value.ToString() + "|";
            }
            else if (prop.Name == "FloorCode")
            {
                var time = int.Parse(prop.GetValue(obj).ToString());
                strMsg += time.ToString("00") + "|" ?? DBNull.Value.ToString() + "|";
            }
            else
            {
                strMsg += GetPropertyByDefault(prop, obj);
                //strMsg += prop.GetValue(objNew).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                //strOld += prop.GetValue(objOld).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
            }
     
            return strMsg;

        }


        List<string> PropertyNameRejected = new List<string>() { "StockNo", "StockSymbol", "TradingDate" };


        private bool PropertyIsRejected(string propertyType)
        {
            return propertyType == PropertyTypeRejected;
        }
        /// <summary>
        /// Remove '|' and '#' from the string
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        private string RemoveFormatterCharacters(string input)
        {            
            return input.Replace("|", "").Replace("#", "");
        }
        
        /// <summary>
        /// Return 0 if property is null or empty
        /// </summary>
        /// <param name="propertyDescriptor"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        private string GetPropertyByDefault(PropertyDescriptor propertyDescriptor, object property)
        {
            string result = "";
            object value = propertyDescriptor.GetValue(property);

            if (value == null)
                result = "0";
            else
                if (value.ToString().Equals(""))
                    result = "0";
                else
                    result = value.ToString();

            RemoveFormatterCharacters(result);

            return value + "|";
        }

        
        private string GetTimeValue(PropertyDescriptor propertyDescriptor, object timeObj)
        {
            int time = int.Parse(propertyDescriptor.GetValue(timeObj).ToString());
            if (time == 0)
                return DateTime.Now.ToString("hh:mm:ss") + "|";
            else
                return time.ToString("00:00:00") + "|" ?? DBNull.Value.ToString() + "|";
        }


        /// <summary>
        /// Build the message StockInfoStatic struct included values and properties of StockInfoStatic
        /// </summary>
        /// <param name="objStatic"></param>
        /// <returns>Return a message string have struct along StockInfoStatic parameter</returns>
        private string BuildMsgStockInfoStatic(PriceLibrary.PriceService.StockInfoStatic objStatic)
        {
            string strMessage = String.Empty;
            try
            {
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.PriceService.StockInfoStatic));
                foreach (PropertyDescriptor prop in properties)
                {
                    if (IsNotRejected(prop))
                    {
                        if (prop.Name == "FloorCode")
                        {
                            var FloorCode = int.Parse(prop.GetValue(objStatic).ToString());
                            strMessage += FloorCode.ToString("00") + "|" ?? DBNull.Value.ToString() + "|";
                        }
                        else
                        {
                            strMessage += GetPropertyStr(prop, objStatic);
                            //strMessage += prop.GetValue(objStatic).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                        }
                        //strMessage += prop.GetValue(objStatic).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                        //strMessage += prop.Name +"="+prop.GetValue(objStatic).ToString().Trim() + "|\n" ?? DBNull.Value.ToString() + "|\n";
                        //strMessage += GetPropertyStr(prop, objStatic);
                    }
                }
            }
            catch (Exception ex)
            {
                ex.Data["StockInfoStatistics"] = objStatic;
                ex.Data["message"] = strMessage;
                throw ex;
            }
            return strMessage;
        }
         /// <summary>
        /// Return Property Name if value is null or empty
        /// </summary>
        /// <param name="propertyDescriptor"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        private string GetPropertyStr(PropertyDescriptor propertyDescriptor, object property)
        {
            object value = propertyDescriptor.GetValue(property);
            if (value==null || value.ToString().Equals(""))
            {
                value = propertyDescriptor.Name;
            }

            return value.ToString().Trim().Replace("|", "").Replace("#","") + "|";
        }

        private bool IsNotRejected(PropertyDescriptor prop)
        {
        return !PropertyNameRejected.Contains(prop.Name) && prop.PropertyType.ToString() != PropertyTypeRejected;
        }

        /// <summary>
        /// Build the message Stock struct included values and properties of new Stock and old Stock
        /// </summary>
        /// <param name="objNew"></param>
        /// <param name="objOld"></param>
        /// <returns>Return a message string have struct along Stock parameter</returns>
        public virtual string BuildMessageStock(PriceLibrary.PriceService.StockInfoStatic objStatic,
            PriceLibrary.PriceService.StockInfoDyns objNew, PriceLibrary.PriceService.StockInfoDyns objOld)
        {
            string strMsgObj = MessageType.Stock.ToString().ToUpper() + "#";
            string strDynsMsgNew = String.Empty;
            string strDynsMsgOld = String.Empty;
            string strStaticMsg = BuildMsgStockInfoStatic(objStatic);
            try
            {
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.PriceService.StockInfoDyns));
                foreach (PropertyDescriptor prop in properties)
                {
                    if (!prop.Name.Trim().ToLower().Equals("StockNo".ToLower()) && !prop.Name.Trim().ToLower().Equals("TradingDate".ToLower()) && prop.PropertyType.ToString() != PropertyTypeRejected)
                    {
                        if (prop.Name == "Time")
                        {
                            strDynsMsgNew += GetTimeValue(prop, objNew);
                            strDynsMsgOld += GetTimeValue(prop, objOld);
                        }
                        else
                        {
                            strDynsMsgNew += prop.GetValue(objNew).ToString().Trim() + "|" ?? prop.Name + "|";
                            strDynsMsgOld += prop.GetValue(objOld).ToString().Trim() + "|" ?? prop.Name + "|";
                        }
                        //strDynsMsgNew += prop.Name.Trim() + "|\n" ?? DBNull.Value.ToString() + "|\n";
                        //strDynsMsgOld += prop.Name.Trim() + "|\n" ?? DBNull.Value.ToString() + "|\n";
                    }
                }
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strStaticMsg + strDynsMsgNew + "#" + strStaticMsg + strDynsMsgOld;
                //_log.WarnFormat("{0}", strMsgObj); 
            }
            catch (Exception ex)
            {
                ex.Data["StockInfoStatic"] = objStatic;
                ex.Data["new StockInfoDyns"] = objNew;
                ex.Data["old StockInfoDyns"] = objOld;
                throw ex;
            }

            return strMsgObj;
        }




        /// <summary>
        /// If symbol is one of HOSE, VN30, UPCOM, HNX, HNX30
        /// </summary>
        /// <param name="symbol"></param>
        /// <returns></returns>
        private bool IsMarketInfoSymbol(string symbol)
        {
            List<string> symbolList = new List<string>(new string[] { "HOSE", "VN30", "UPCOM", "HNX", "HNX30" });
            if (symbolList.Contains(symbol))
                return true;
            return false;
        }


        // private DataSource _dataSource;

        private Dictionary<string, PriceLibrary.PriceService.StockBase> _basePrices;

        public void SetBasePrices(Dictionary<string, PriceLibrary.PriceService.StockBase> basePrices)
        {
            _basePrices = basePrices;
        }

        /// <summary>
        /// Build message transaction : concat 2 obj new & old
        /// </summary>        
        public virtual string BuildMessageTransaction(PriceLibrary.ChartService.Transaction objNew,
            PriceLibrary.ChartService.Transaction objOld)
        {
            return "";
            if (_basePrices == null)
                throw new NullReferenceException("BuildMessageTransaction: DataSource BasePrices is not intiailzed yet");

            string strMsgObj = MessageType.Transaction.ToString().ToUpper() + "#";
            string strNew = String.Empty;
            string strOld = String.Empty;
            string FloorCode = "10";

            if (objNew.MatchType == null)
            {
                objNew.MatchType = "U";
                objOld.MatchType = "U";
            }

            try
            {
                FloorCode = GetFloorCode(objNew, _basePrices);
            }
            catch (KeyNotFoundException ex)
            {
                throw new KeyNotFoundException("Key not found in buildilng message transaction", ex);
                //_log.Error("BuildMessageTransaction error. Not found Symbol  " + objNew.Symbol + " in Datasource BasePrices", ex);
            }

            string actual = FloorCode;

            if (IsMarketInfoSymbol(objNew.Symbol))
                FloorCode = GetFloorCode(objNew.Symbol);
            else ///Get FloorCode From datasource.BasePrices base on Transaction.Symbol
            {
                try
                {
                    var stockstatic = (PriceLibrary.PriceService.StockInfoStatic)_basePrices[objNew.Symbol];
                    FloorCode = stockstatic.FloorCode.ToString();
                }
                catch (Exception ex)
                {
                    throw new Exception("BuildMessageTransaction error. Not found Symbol  " + objNew.Symbol + " in Datasource BasePrices", ex);
                    // _log.Error("BuildMessageTransaction error. Not found Symbol  " + objNew.Symbol + " in Datasource BasePrices", ex);
                }
            }

            try
            {
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(PriceLibrary.ChartService.Transaction));
                foreach (PropertyDescriptor prop in properties)
                {
                    if (prop.PropertyType.ToString() != PropertyTypeRejected)
                    {
                        strNew += GetPropertyTransactionStr(objNew, prop);
                        strOld += GetPropertyTransactionStr(objOld, prop);
                    }
                }
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strNew + FloorCode + "#" + strOld + FloorCode;
            }
            catch (Exception ex)
            {
                throw new Exception("Error BuildMessageTransaction", ex);
                //_log.Error("Error BuildMessageTransaction", ex);
            }

            return strMsgObj;
        }

        private string GetPropertyTransactionStr(PriceLibrary.ChartService.Transaction transaction, PropertyDescriptor propertyDescriptor)
        {
            if (propertyDescriptor.PropertyType.ToString() != PropertyTypeRejected)
            {
                if (propertyDescriptor.Name == "Time")
                {
                    return GetTimeValue(propertyDescriptor, transaction);
                    //var time = int.Parse(propertyDescriptor.GetValue(transaction).ToString());
                    //return time.ToString("00:00:00") + "|" ?? DBNull.Value.ToString() + "|";
                }
                else if (propertyDescriptor.Name == "FloorCode")
                {
                    var floorCode = int.Parse(propertyDescriptor.GetValue(transaction).ToString());
                    return floorCode.ToString("00") + "|" ?? DBNull.Value.ToString() + "|";
                }
                else if (propertyDescriptor.Name == "LastVol")
                {
                    long lastVol = Convert.ToInt64(transaction.LastVol);
                    return lastVol.ToString() + "|" ?? DBNull.Value.ToString() + "|";
                }
                else
                {
                    return propertyDescriptor.GetValue(transaction).ToString().Trim() + "|" ?? DBNull.Value.ToString() + "|";
                }
            }
            else
                throw new Exception("PropertyTypeRejected, do not know which string to return");
        }

        /// <summary>
        /// Mapped symbol to FloorCode:
        /// HOSE, VN30 >> "10"
        /// UPCOM >> "03"
        /// HNX, HNX30 >> "02"
        /// throw Exception otherwise
        /// </summary>
        /// <param name="symbol"></param>
        /// <returns></returns>
        private string GetFloorCode(string symbol)
        {
            if (symbol.Equals("HOSE", StringComparison.Ordinal) || symbol.Equals("VN30", StringComparison.Ordinal))
                return "10";
            if (symbol.Equals("UPCOM", StringComparison.Ordinal))
                return "03";
            if (symbol.Equals("HNX", StringComparison.Ordinal) || symbol.Equals("HNX30", StringComparison.Ordinal))
                return "02";
            else
                throw new Exception("Invalid Input");
        }


        /// <summary>
        /// Get floor code if is a market info transaction symbol (HOSE, HNX etc)
        /// otherwise get the floor of that symbol from datasource
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="dataSource"></param>
        /// <returns></returns>
        private string GetFloorCode(PriceLibrary.ChartService.Transaction obj, Dictionary<string, PriceLibrary.PriceService.StockBase> basePrices)
        {
            if (IsMarketInfoSymbol(obj.Symbol))
                return GetFloorCode(obj.Symbol);
            else
            {
                if (basePrices.ContainsKey(obj.Symbol))
                {
                    var stockstatic = (PriceLibrary.PriceService.StockInfoStatic)basePrices[obj.Symbol];
                    return stockstatic.FloorCode.ToString();
                }
                else
                    throw new KeyNotFoundException("Not found symbol in DataSource: " + obj.Symbol);
            }
        }

        /// <summary>
        /// Build the message Buy Put Through struct included values and properties of new Stock Buy and old Stock Buy
        /// </summary>
        /// <param name="objStatic"></param>
        /// <param name="objNew"></param>
        /// <param name="objOld"></param>
        /// <returns>Return a message string have struct along Stick Buy parameter</returns>
        public string BuildMessagePutThrough(PriceLibrary.PriceService.StockInfoStatic objStatic, PriceLibrary.PriceService.StockInfoDyns objNew, PriceLibrary.PriceService.StockInfoDyns objOld, TransactionType transType)
        {
            throw new NotImplementedException();
        }

        public string BuildMessageTransactionPutThrough(PriceLibrary.PriceService.Transaction objNew,
        PriceLibrary.PriceService.Transaction objOld)
        {
            throw new NotImplementedException();
        }

    }
}
